// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package types2

import (
	"cmd/compile/internal/syntax"
	"strconv"
)

// ----------------------------------------------------------------------------------------
// API 

// 结构表示结构类型。
type Struct struct {
	fields []*Var   // 字段！=nil表示结构已设置（可能带有len（fields）==0）
	tags   []string // 字段标记；nil如果没有标记
}

// NewStruct返回一个包含给定字段和相应字段标记的新结构。
// 如果索引为i的字段有一个标记，则标记[i]必须是该标记，但len（标记）可能是
// 只要需要保存索引为i最大的标记即可。因此，
// 如果没有字段有标记，则标记可能为零。
func NewStruct(fields []*Var, tags []string) *Struct {
	var fset objset
	for _, f := range fields {
		if f.name != "_" && fset.insert(f) != nil {
			panic("multiple fields with the same name")
		}
	}
	if len(tags) > len(fields) {
		panic("more tags than fields")
	}
	s := &Struct{fields: fields, tags: tags}
	s.markComplete()
	return s
}

// NumFields返回结构中的字段数（包括空白字段和嵌入字段）。
func (s *Struct) NumFields() int { return len(s.fields) }

// 字段返回0<=i<NumFields（）的第i个字段。
func (s *Struct) Field(i int) *Var { return s.fields[i] }

// Tag返回0<=i<NumFields（）的第i个字段标记。
func (s *Struct) Tag(i int) string {
	if i < len(s.tags) {
		return s.tags[i]
	}
	return ""
}

func (s *Struct) Underlying() Type { return s }
func (s *Struct) String() string   { return TypeString(s, nil) }

// ---------------------------------------------------------------
// 实现

func (s *Struct) markComplete() {
	if s.fields == nil {
		s.fields = make([]*Var, 0)
	}
}

func (check *Checker) structType(styp *Struct, e *syntax.StructType) {
	if e.FieldList == nil {
		styp.markComplete()
		return
	}

	// 结构字段和标记
	var fields []*Var
	var tags []string

	// 对于双重声明检查
	var fset objset

	// 当前字段类型和标记
	var typ Type
	var tag string
	add := func(ident *syntax.Name, embedded bool, pos syntax.Pos) {
		if tag != "" && tags == nil {
			tags = make([]string, len(fields))
		}
		if tags != nil {
			tags = append(tags, tag)
		}

		name := ident.Value
		fld := NewField(pos, check.pkg, name, typ, embedded)
		// 规范：“在结构中，非空字段名称必须是唯一的。”
		if name == "_" || check.declareInSet(&fset, pos, fld) {
			fields = append(fields, fld)
			check.recordDef(ident, fld)
		}
	}

	// addInvalid将无效类型的嵌入字段添加到
	// 有错误的字段的结构中；这将使结构字段的数量与源保持同步，只要字段是uu或具有不同的名称u。
	addInvalid := func(ident *syntax.Name, pos syntax.Pos) {
		typ = Typ[Invalid]
		tag = ""
		add(ident, true, pos)
	}

	var prev syntax.Expr
	for i, f := range e.FieldList {
		// 使用相同类型（例如：a、b、c T）语法声明的字段
		// 共享相同的类型表达式。如果是新类型，则只检查类型。
		if i == 0 || f.Type != prev {
			typ = check.varType(f.Type)
			prev = f.Type
		}
		tag = ""
		if i < len(e.TagList) {
			tag = check.tag(e.TagList[i])
		}
		if f.Name != nil {
			// 命名字段
			add(f.Name, false, f.Name.Pos())
		} else {
			// 嵌入字段
			// 规范：“必须将嵌入类型指定为类型名T或
			// 指向非接口类型名*T的指针，并且T本身不能是
			// 指针类型。”
			pos := syntax.StartPos(f.Type)
			name := embeddedFieldIdent(f.Type)
			if name == nil {
				check.errorf(pos, "invalid embedded field type %s", f.Type)
				name = &syntax.Name{Value: "_"} // TODO（gri）需要将position设置为pos 
				addInvalid(name, pos)
				continue
			}
			add(name, true, pos)

			// 因为我们有一个名称，typ必须是T或*T的形式，其中T是（命名或别名）类型的名称
			// ，t（=deref（typ））必须是t的类型。
			// 我们必须将此检查推迟到最后，因为我们不想将
			// （通过在（t）下）实例化为可能不完整的类型。
			embeddedTyp := typ // 对于低于
			embeddedPos := pos
			check.later(func() {
				t, isPtr := deref(embeddedTyp)
				switch u := under(t).(type) {
				case *Basic:
					if t == Typ[Invalid] {
						// 的闭包，在
						return
					}
					// 之前报告了错误。指针被视为常规指针
					if u.kind == UnsafePointer {
						check.error(embeddedPos, "embedded field type cannot be unsafe.Pointer")
					}
				case *Pointer:
					check.error(embeddedPos, "embedded field type cannot be a pointer")
				case *Interface:
					if isTypeParam(t) {
						check.error(embeddedPos, "embedded field type cannot be a (pointer to a) type parameter")
						break
					}
					if isPtr {
						check.error(embeddedPos, "embedded field type cannot be a pointer to an interface")
					}
				}
			}).describef(embeddedPos, "check embedded type %s", embeddedTyp)
		}
	}

	styp.fields = fields
	styp.tags = tags
	styp.markComplete()
}

func embeddedFieldIdent(e syntax.Expr) *syntax.Name {
	switch e := e.(type) {
	case *syntax.Name:
		return e
	case *syntax.Operation:
		if base := ptrBase(e); base != nil {
			// /*T有效，但**T不是
			if op, _ := base.(*syntax.Operation); op == nil || ptrBase(op) == nil {
				return embeddedFieldIdent(e.X)
			}
		}
	case *syntax.SelectorExpr:
		return e.Sel
	case *syntax.IndexExpr:
		return embeddedFieldIdent(e.X)
	}
	return nil // 无效的嵌入字段
}

func (check *Checker) declareInSet(oset *objset, pos syntax.Pos, obj Object) bool {
	if alt := oset.insert(obj); alt != nil {
		var err error_
		err.errorf(pos, "%s redeclared", obj.Name())
		err.recordAltDecl(alt)
		check.report(&err)
		return false
	}
	return true
}

func (check *Checker) tag(t *syntax.BasicLit) string {
	// 如果T.不正确，则在解析过程中报告错误。
	if t != nil && !t.Bad {
		if t.Kind == syntax.StringLit {
			if val, err := strconv.Unquote(t.Value); err == nil {
				return val
			}
		}
		check.errorf(t, invalidAST+"incorrect tag syntax: %q", t.Value)
	}
	return ""
}

func ptrBase(x *syntax.Operation) syntax.Expr {
	if x.Op == syntax.Mul && x.Y == nil {
		return x.X
	}
	return nil
}
